import PubSub.*;

import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;

import java.util.*;
import java.text.*;

public class TopicImpl extends TopicPOA {
	private String name;
	private List<String> subscriptions;
	private Map<String,Date> lastSeen;
	private List<Message> messages;
	//                              Wed, 1 Apr 2009 18:26:57
	private String dateFormatter = "EEE, d MMM yyyy HH:mm:ss";
	private ORB orb;
	private NamingContextExt rootContext;
	private NamingContextExt clientContext;
	
	
	public TopicImpl(String name, ORB orb) {
		this.name = name;
		this.orb = orb;
		this.subscriptions = new ArrayList<String>();
		this.lastSeen = new HashMap<String,Date>();
		this.messages = new ArrayList<Message>();
		
		try {
			rootContext = NamingContextExtHelper.narrow(orb.resolve_initial_references("NameService"));
			clientContext = NamingContextExtHelper.narrow(rootContext.resolve(rootContext.to_name("Client")));
		} catch(Exception e) {
			System.out.println("ERROR: "+e.getMessage());
		}
	}
	
	
	public synchronized void addSubscriber(Credential credential) throws AlreadySubscribed {
		String key = Hash.getHashText(credential.username+credential.password);
		if(subscriptions.indexOf(key)<0) {
			subscriptions.add(key);
			System.out.println("Added "+credential.username+" in topic "+name);
		} else {
			throw new AlreadySubscribed();
		}
	}
	
	public synchronized void removeSubscriber(Credential credential) throws NotSubscribedUser {
		String key = Hash.getHashText(credential.username+credential.password);
		if(subscriptions.indexOf(key)>=0) {
			subscriptions.remove(subscriptions.indexOf(key));
			System.out.println("Removed "+credential.username+" in topic "+name);
			lastSeen.remove(key);
		} else {
			throw new NotSubscribedUser();
		}
	}
	
	public synchronized void addMessage(String text, Credential credential) {
		String key = Hash.getHashText(credential.username+credential.password);
		DateFormat df = new SimpleDateFormat(dateFormatter, Locale.UK);
		Message message = new Message(name, credential.username, df.format(new Date()), text);
		messages.add(message);
		System.out.println("Added new message by "+credential.username+" in topic "+name);
		for(String clientRef : subscriptions) {
			try {
				// resolve the client in naming
				org.omg.CORBA.Object objRef = clientContext.resolve_str(clientRef);
				Subscriber s = SubscriberHelper.narrow(objRef);
				s.newMessage(message);
				Credential c = s.getCredential();
				if(subscriptions.indexOf(Hash.getHashText(c.username+c.password))>=0) {
					lastSeen.put(Hash.getHashText(c.username+c.password), new Date());
				}
			} catch(NotFound e) { 
				System.out.println("WARNING: subscriber "+clientRef+" is likely to be off-line");
			} catch(Exception e) {
				System.out.println("ERROR: "+e.getMessage());
			}
		}
	}

	public boolean hasSubscriber(Credential credential) {
		String key = Hash.getHashText(credential.username+credential.password);
		return subscriptions.indexOf(key)>=0;
	}
	
	public Message[] getMessageList(Credential credential) throws NotSubscribedUser {
		String key = Hash.getHashText(credential.username+credential.password);
		if(subscriptions.indexOf(key)>=0) {
			System.out.println("Sent message list of "+name+" to "+credential.username);
			lastSeen.put(key, new Date());
			return messages.toArray(new Message[messages.size()]);
		} else {
			throw new NotSubscribedUser();
		}
	}
	
	public Message[] getUnseenMessageList(Credential credential) throws NotSubscribedUser {
		String key = Hash.getHashText(credential.username+credential.password);
		Message [] list;
		if(subscriptions.indexOf(key)>=0) {
			if(lastSeen.get(key)==null) {
				list = messages.toArray(new Message[messages.size()]);
			} else {
				List<Message> unseen = new ArrayList<Message>();
				DateFormat df = new SimpleDateFormat(dateFormatter, Locale.UK);

				for(Message m : messages) {
					if(df.parse(m.date, new ParsePosition(0)).after(lastSeen.get(key))) {
						unseen.add(m);
					}
				}
				list = unseen.toArray(new Message[unseen.size()]);
			}
			System.out.println("Sent unseen message list of "+name+" to "+credential.username);
			lastSeen.put(key, new Date());
			return list;
		} else {
			throw new NotSubscribedUser();
		}
	}
	
	public void eraseStatus() {
		subscriptions.clear();
		lastSeen.clear();
		messages.clear();
		System.out.println("Cleared status of topic "+name);
	}
	
}